package main

import (
	"3nets.io/wireguardcommon"
	"encoding/base64"
	"fmt"
	"log"
	"net"
	"strconv"
	"strings"
)

func SocketClient(ip string, port int, message string) {
	addr := strings.Join([]string{ip, strconv.Itoa(port)}, ":")
	conn, err := net.Dial("tcp", addr)

	if err != nil {
		log.Fatalln(err)
	}

	//Keeping this code as a reference to the ResolveHostIP
	//log.Printf("connected on %s\n", conn.LocalAddr())
	//ipaddr := conn.LocalAddr().(*net.TCPAddr)
	//fmt.Printf("got ipaddr = %s\n", ipaddr)
	//_ = wireguardcommon.ResolveHostIp()

	defer func(conn net.Conn) {
		err := conn.Close()
		if err != nil {
			fmt.Println("error closing edge client connection to " + ip + ":" + string(rune(port)))
		}
	}(conn)

	_, err = conn.Write([]byte(message))
	if err != nil {
		log.Printf("failed to send message: %s\n", message)
		return
	}
	log.Printf("Send: %s", message)

	managerDHPublic := make([]byte, 1024)
	n, _ := conn.Read(managerDHPublic)
	managerDHPublicStr := string(managerDHPublic[:n])
	log.Printf("managerDHPublic: %s, n=%d", managerDHPublic[:n], n)

	presharedkey, edgeDHPublic, err := wireguardcommon.GenPskOnEdge(string(managerDHPublic[:n]))
	if err != nil {
		fmt.Printf("Failed to generate edge presharedkey: %s\n", err)
	} else {
		fmt.Printf("edgeDHPublic = %s\n", edgeDHPublic)
		fmt.Printf("presharedkey = %s\n", presharedkey)
	}

	_, err = conn.Write([]byte(edgeDHPublic))
	if err != nil {
		fmt.Printf("Failed to send edgeDHpublic, err: %s\n", err)
		return
	}

	///////////////////////////////
	// derive edge keys         //
	/////////////////////////////
	edgeWgPrivateStr, _, err := wireguardcommon.CreateKeyPairFromSeed(edgeSignature)
	if err != nil {
		fmt.Printf("Failed to generate manager private/public key pair: %s\n", err)
		return
	}
	///////////////////////////////
	// end of derive edge keys  //
	/////////////////////////////

	encryptedAck := make([]byte, 1024)
	n, _ = conn.Read(encryptedAck)
	log.Printf("encryptedAck: %x, n=%d\n", encryptedAck[:n], n)
	secret, _ := base64.StdEncoding.DecodeString(presharedkey)
	ack, _ := wireguardcommon.Chacha20poly1305Decrypt(secret, encryptedAck[:n])
	log.Printf("ack: %x\n", ack)
	tokens := strings.Split(string(ack), ":")
	var endpointIP string
	var myWgIpAndMask string
	if len(tokens) >= 2 && tokens[0] == "OK" {
		endpointIP = tokens[1]
	}

	if len(tokens) >= 3 && tokens[0] == "OK" {
		myWgIpAndMask = tokens[2]
	}
	fmt.Printf("got OK and endpointIP %s\n", endpointIP)
	fmt.Printf("my ip and mask %s\n", myWgIpAndMask)

	edgeWgPrivateKey, err := base64.StdEncoding.DecodeString(edgeWgPrivateStr)
	if err != nil {
		fmt.Printf("failed to parse edgeWgPrivateStr: %s, err  %s\n", edgeWgPrivateStr, err)
		return
	}
	_, wireguardLink, err := wireguardcommon.CreateWireguardInterface(edgeWgPrivateKey, myWgIpAndMask)
	if err != nil {
		fmt.Printf("failed to create wireguard interface %s\n", err)
		return
	}

	managerIp, _ := wireguardcommon.GetFirstAddr()
	fmt.Printf("adding peer ..\n")
	wireguardcommon.AddPeer(presharedkey, managerDHPublicStr, endpointIP, managerIp.String(), true)
	fmt.Printf("adding linkAddr ..\n")
	err = wireguardcommon.AddLinkAddress(wireguardLink, myWgIpAndMask, managerIp.String())
	if err != nil {
		fmt.Printf("failed at AddLinkAddress, err: %s\n", err)
		return
	}
}

var edgeSignature = "BOI8PrXNaVANosmOgylvmCMpL7Ek3AEAmvkrAJJ6fKxoJQTh1w0Tksv3uvXAVsxmfXeKE97il70Q8blrvu5rAjNuZXRzZWRnZS5pbw=="

func main() {
	SocketClient(wireguardcommon.Ip2, wireguardcommon.InitialPort, edgeSignature)
}
